MyImobee — Planejamento
Fase 1 · Planejamento e design

MyImobee: a vitrine digital do estoque da imobiliária.

Plataforma SaaS multi-tenant para imobiliárias e corretores regionais: portal público rápido e indexável, painel administrativo e cadastro assistido por IA com revisão humana obrigatória. Este documento cobre a arquitetura, o banco, os fluxos, o mapa de telas e as estratégias de autenticação, isolamento, IA e imagens. As telas estão no protótipo navegável.

Next.js + TypeScriptSupabase · PostgreSQL · RLSStorage + CDNClaude API via servidor

1. Arquitetura proposta

Um único código Next.js serve os três frontends (portal, painel, master) com separação por rota e por permissão. Tudo que exige chave ou privilégio roda no servidor. O banco é a fonte da verdade e o isolamento entre imobiliárias é garantido no próprio Postgres.

FRONTEND
Portal público
SSR/ISR, SEO, busca, imóvel, favoritos
FRONTEND
Painel da imobiliária
Dashboard, imóveis, cadastro IA, leads
FRONTEND
Painel master
Imobiliárias, planos, consumo, métricas
EDGE · MIDDLEWARE
CDN + resolução de tenant pelo domínio
host → agency_domains → agency_id · cache de páginas públicas · rate limiting
API
Route Handlers / Server Actions
Validação (Zod), permissões, orquestração
SERVIÇO
IA (Edge Function)
Extração, descrição, SEO · chave só no servidor
SERVIÇO
Processamento de imagens
Worker com fila: WebP/AVIF, thumbs, hash
SERVIÇO
Eventos e integrações
Webhooks, feeds XML, automações
Supabase Auth
Sessões, JWT com agency_id e perfil
PostgreSQL + RLS
Fonte da verdade, isolamento por tenant
Storage
Buckets por tenant, políticas de acesso
Filas e cron
Jobs de IA, imagens, sitemap
Por que não um monolito: os serviços de IA e imagem são assíncronos e isolados; falham e escalam sem derrubar o portal.
Por que não microsserviços: no início, um app Next.js com módulos bem separados mantém o time pequeno produtivo.
Estrutura de pastas: app/(portal), app/(painel), app/(master), lib/domain, supabase/migrations, supabase/functions.

2. Estrutura inicial do banco

Toda tabela de negócio carrega agency_id. Chaves UUID, created_at/updated_at em todas, exclusão lógica onde a URL precisa sobreviver.

{{ t.name }}{{ t.tag }}
{{ t.desc }}
{{ c }}
Enums: property_status (rascunho, publicado, reservado, vendido, alugado, inativo) · purpose (venda, aluguel) · role (admin, imobiliaria, corretor) · badge (destaque, novidade, oportunidade, exclusivo). Código do imóvel: sequência por tenant com prefixo configurável (IMB-1024), índice único (agency_id, code) e busca por código no portal.

3. Fluxos de usuário

{{ f.title }}
{{ s }}
{{ f.note }}

4. Mapa de telas e URLs

{{ g.title }}
{{ i.url }}{{ i.label }}

5. Componentes do design system

Fundação: tema escuro de baixa saturação, Inter peso 500 nos títulos, raio de 8px, acento usado como linha e brilho — não como preenchimento. Botão principal com contorno, foco visível no acento, fotos grandes.

{{ c.name }}
{{ c.desc }}
Estados em todos: loading (skeleton ou spinner no próprio botão), empty (mensagem + próxima ação), error (mensagem inline + tentar novamente), success (toast e confirmação), disabled (45% de opacidade, cursor bloqueado).

6. Autenticação e perfis

  • Supabase Auth com e-mail e senha, link mágico e Google. Sessão em cookie httpOnly, lida no servidor (SSR).
  • Um Custom Access Token Hook injeta no JWT o agency_id ativo e o role vindos de memberships. Um usuário pode pertencer a mais de uma imobiliária e alternar.
  • Perfis: admin (MyImobee, acesso global), imobiliaria (tudo do próprio tenant), corretor (cria e edita os próprios imóveis e leads). Permissões finas em role_permissions, prontas para novos níveis.
  • Master com MFA obrigatório. “Acessar painel” de uma imobiliária gera sessão de suporte temporária e registrada em audit_logs.
  • Favoritos começam em localStorage; com conta de visitante, sincronizam na tabela favorites (merge no primeiro login).

7. Estratégia multi-tenant

  • Banco compartilhado, isolamento por linha: agency_id em todas as tabelas de negócio + Row Level Security em todas elas. Uma imobiliária nunca lê dados de outra, nem se a API tiver um bug.
  • Resolução pelo domínio: o middleware lê o host (caririprime.myimobee.com.br ou www.caririprime.com.br), consulta agency_domains (em cache) e injeta o tenant no contexto da requisição. myimobee.com.br é o portal agregado.
  • Storage: caminhos {agency_id}/{property_id}/{media_id}.webp com políticas que conferem o agency_id do JWT.
  • Chave service_role só em Edge Functions e jobs; o navegador usa apenas a chave anônima, sempre sob RLS.
  • Limites do plano aplicados no servidor (imóveis, usuários, armazenamento, operações de IA, destaques, domínio próprio).
create function auth.agency_id() returns uuid language sql stable as $$
  select nullif(auth.jwt() ->> 'agency_id', '')::uuid $$;

alter table properties enable row level security;

-- leitura pública: somente anúncios visíveis
create policy "portal lê anúncios visíveis" on properties for select
  using (status in ('publicado','reservado','vendido','alugado'));

-- equipe da imobiliária: tudo do próprio tenant
create policy "equipe gerencia o tenant" on properties for all
  using (agency_id = auth.agency_id())
  with check (agency_id = auth.agency_id());

-- corretor: só edita o que é dele
create policy "corretor edita os próprios" on properties for update
  using (agency_id = auth.agency_id()
     and (auth.jwt() ->> 'role' <> 'corretor' or broker_id = auth.uid()));

8. Fluxo do cadastro com IA

{{ s.label }}
  • Entrada: texto livre, fotos (até 40) e, na próxima fase, PDF/Word/planilha (extração de texto no servidor antes da IA).
  • Extração estruturada: a IA devolve JSON validado por schema; cada campo vem com source (texto, foto ou nulo). Campo ausente = null → “Não informado”.
  • Inferência visual nunca vira fato: aparece como “Possível característica — confirmar antes de publicar” e só entra no anúncio se o usuário confirmar.
  • Descrição (Objetivo, Comercial, Premium, SEO) e SEO são gerados só a partir dos campos confirmados.
  • Revisão humana obrigatória: o resultado é salvo como rascunho; a publicação exige clique em “Publicar imóvel” e confirmação explícita.
  • Rastreio e custo: cada chamada grava ai_jobs (tenant, tipo, tokens, custo, duração, erro). Limite mensal por plano e rate limit por usuário.
  • Falha: sem resposta da IA, o sistema faz leitura básica local e marca tudo para revisão — o corretor nunca fica bloqueado.

9. Fluxo de upload de imagens

{{ s }}
  • Pré-processamento no navegador (redimensiona para 1600px e converte para WebP) reduz o upload em 80–95% em redes móveis — já funcionando no protótipo.
  • Upload direto ao Storage por URL assinada; o servidor valida tipo real pelos magic bytes, tamanho (15 MB) e quantidade por imóvel.
  • Worker gera variantes 1600/960/480/320 em AVIF e WebP, remove EXIF/GPS, calcula blurhash (placeholder), hash perceptual (duplicadas) e nota de nitidez (baixa qualidade).
  • property_media guarda ordem, principal, legenda, alt e ambiente. Entrega por CDN com srcset, lazy loading e cache imutável.

10. Integrações

IntegraçãoAgoraDepois
{{ i.n }}{{ i.now }}{{ i.later }}

11. SEO e performance

  • URLs amigáveis estáveis: /imovel/casa-3-quartos-salesianos-juazeiro-do-norte/1024. O número final é a chave; se o slug mudar, 301 para o novo.
  • Páginas indexáveis de cidade, bairro e tipo: /imoveis/juazeiro-do-norte-ce, /imoveis/juazeiro-do-norte-ce/casas, com texto local e links internos.
  • Gerados automaticamente: title, meta description, Open Graph (imagem principal 1200×630), canonical, breadcrumb (BreadcrumbList) e JSON-LD RealEstateListing + Offer.
  • Vendido/alugado mantém a URL no ar com o aviso e imóveis semelhantes — preserva o SEO e aproveita o tráfego.
  • sitemap.xml e robots.txt por tenant; ISR com revalidação ao publicar/editar.
  • Meta: LCP abaixo de 2,5 s em 4G, CLS abaixo de 0,1. Imagem hero com prioridade, restante lazy, fontes com display=swap, JS mínimo no portal.

12. Segurança

{{ s }}

13. Fases de implementação

FASE {{ p.n }}
{{ p.name }}
{{ p.items }}
{{ p.status }}